// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

// NOTE: API is EXPERIMENTAL and will change without going through a
// deprecation cycle

#pragma once

#include <memory>
#include <string>
#include <vector>


#include <turbo/utility/status.h>
#include <turbo/base/macros.h>

namespace nebula::compute {

    class Function;

    class FunctionOptionsType;

    /// \brief A mutable central function registry for built-in functions as well
    /// as user-defined functions. Functions are implementations of
    /// nebula::compute::Function.
    ///
    /// Generally, each function contains kernels which are implementations of a
    /// function for a specific argument signature. After looking up a function in
    /// the registry, one can either execute it eagerly with Function::execute or
    /// use one of the function's dispatch methods to pick a suitable kernel for
    /// lower-level function execution.
    class TURBO_EXPORT FunctionRegistry {
    public:
        ~FunctionRegistry();

        /// \brief Construct a new registry.
        ///
        /// Most users only need to use the global registry.
        static std::unique_ptr<FunctionRegistry> create();

        /// \brief Construct a new nested registry with the given parent.
        ///
        /// Most users only need to use the global registry. The returned registry never changes
        /// its parent, even when an operation allows overwriting.
        static std::unique_ptr<FunctionRegistry> create(FunctionRegistry *parent);

        /// \brief Check whether a new function can be added to the registry.
        ///
        /// \returns turbo::already_exists_error if a function with the same name is already registered.
        turbo::Status can_add_function(std::shared_ptr<Function> function, bool allow_overwrite = false);

        /// \brief Add a new function to the registry.
        ///
        /// \returns turbo::already_exists if a function with the same name is already registered.
        turbo::Status add_function(std::shared_ptr<Function> function, bool allow_overwrite = false);

        /// \brief Check whether an alias can be added for the given function name.
        ///
        /// \returns turbo::not_found_error if the function with the given name is not registered.
        turbo::Status can_add_alias(const std::string &target_name, const std::string &source_name);

        /// \brief Add alias for the given function name.
        ///
        /// \returns turbo::not_found_error if the function with the given name is not registered.
        turbo::Status add_alias(const std::string &target_name, const std::string &source_name);

        /// \brief Check whether a new function options type can be added to the registry.
        ///
        /// \return turbo::already_exists if a function options type with the same name is already
        /// registered.
        turbo::Status can_add_function_options_type(const FunctionOptionsType *options_type,
                                                bool allow_overwrite = false);

        /// \brief Add a new function options type to the registry.
        ///
        /// \returns turbo::already_exists if a function options type with the same name is already
        /// registered.
        turbo::Status add_function_options_type(const FunctionOptionsType *options_type,
                                             bool allow_overwrite = false);

        /// \brief Retrieve a function by name from the registry.
        turbo::Result<std::shared_ptr<Function>> get_function(const std::string &name) const;

        /// \brief Return vector of all entry names in the registry.
        ///
        /// Helpful for displaying a manifest of available functions.
        std::vector<std::string> get_function_names() const;

        /// \brief Retrieve a function options type by name from the registry.
        turbo::Result<const FunctionOptionsType *> get_function_options_type(
                const std::string &name) const;

        /// \brief The number of currently registered functions.
        int num_functions() const;

        /// \brief The cast function object registered in add_function.
        ///
        /// Helpful for get cast function as needed.
        const Function *cast_function() const;

    private:
        FunctionRegistry();

        // Use PIMPL pattern to not have std::unordered_map here
        class FunctionRegistryImpl;

        std::unique_ptr<FunctionRegistryImpl> impl_;

        explicit FunctionRegistry(FunctionRegistryImpl *impl);
    };

}  // namespace nebula::compute
